﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SIA.Configurators;
using System.Net.Sockets;
using System.Timers;

namespace SIA.HwAccess.PLCSiemens.ConnectionDriver
{
    public class TCPFetchWriteConnection : HwConnection
    {
        private IHwDevice _device;

        private TCPFetchConnection _fetchConnection;
        private TCPWriteConnection _writeConnection;

        private CompactHwAddressCollection _addressCollection = new CompactHwAddressCollection();
        private IDictionaryProperty _signalsProperty;
        private IDictionaryProperty _connectionProperty;
        private Timer _timer;
        private Double _interval;

        private eConnectionStatus _oldStatus = eConnectionStatus.UNDEF;

        public override eConnectionStatus Status
        {
            get
            {
                if (!_timer.Enabled || _writeConnection.Status == eConnectionStatus.DISCONNECT || _fetchConnection.Status == eConnectionStatus.DISCONNECT) return eConnectionStatus.DISCONNECT;
                if (_timer.Enabled && _writeConnection.Status == eConnectionStatus.CONNECT && _fetchConnection.Status == eConnectionStatus.CONNECT) return eConnectionStatus.CONNECT;
                if (_writeConnection.Status == eConnectionStatus.ERROR || _fetchConnection.Status == eConnectionStatus.ERROR) return eConnectionStatus.ERROR;
                return eConnectionStatus.UNDEF;
            }
        }

        public TCPFetchWriteConnection(IDictionaryProperty connectionProperty, IDictionaryProperty signalsProperty, IHwDevice device)               
        {
            _rawData = new S7RawData();

            this._name = connectionProperty["NOME"].PropertyValue;
            this._signalsProperty = signalsProperty;
            this._connectionProperty = connectionProperty;
            this._device = device;            
            this._writeConnection = (_device.Connections[_connectionProperty.Find("WriteConnection").PropertyValue] as TCPWriteConnection);
            this._fetchConnection = (_device.Connections[_connectionProperty.Find("FetchConnection").PropertyValue] as TCPFetchConnection);
            _interval = connectionProperty.ContainsKey("SCANRATE") ? Double.Parse(connectionProperty["SCANRATE"].PropertyValue) : 1000;

            foreach (IDictionaryProperty dP in signalsProperty.Values)
                if (dP["CONNESSIONE"].PropertyValue == this.Name)
                    switch (dP["TIPO"].PropertyValue.ToUpper())
                    {
                        case "S7BOOLEAN":
                            Signals.Add(dP["NOMESEGNALE"].PropertyValue, new S7Boolean(dP, this));
                            break;
                        case "S7BYTE":
                            Signals.Add(dP["NOMESEGNALE"].PropertyValue, new S7Byte(dP, this));
                            break;
                        case "S7INT":
                            Signals.Add(dP["NOMESEGNALE"].PropertyValue, new S7Int(dP, this));
                            break;
                        case "S7WORD":
                            Signals.Add(dP["NOMESEGNALE"].PropertyValue, new S7Word(dP, this));
                            break;
                        default:
                            throw new Exception("Segnale Tipo: '" + dP["TIPO"].PropertyValue + "' non supportato");
                    }
            foreach (IDictionaryProperty tagP in signalsProperty.Values)
                  if (tagP["CONNESSIONE"].PropertyValue == _connectionProperty["NOME"].PropertyValue)
                    _addressCollection.Add(new S7HwAddress(tagP["INDIRIZZO"].PropertyValue).Round2Word());


            foreach (IHwSignal s in _signals.Values)
            {
                IHwAddress a = ((S7Signal)s).Address;
                if (!_rawData.ContainsKey(a.DataKey))
                    _rawData.Add(a.DataKey, new Byte[4096]);
                if (_rawData[a.DataKey].Length < a.LenByte + a.StartByte)
                    _rawData[a.DataKey] = new Byte[4096];

                _fetchConnection.register(s);
                _writeConnection.register(s);
                s.Connection = this;
            }

            _timer = new Timer(_interval);
            _timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
            _timer.Enabled = true;

            _fetchConnection.DataArrived += new ConnectionDataEvent(_fetchConnection_DataArrived);            
        }

        void _timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (this.Status != _oldStatus)
            {
                if (StatusChange != null) StatusChange(this, _oldStatus);
                _oldStatus = Status;
            }
        }

        public override void requestWriteData(IHwAddress address, int offset, byte value)
        {
            base.requestWriteData(address, offset, value);
            _writeConnection.requestWriteData(address, offset, value);
        }

        void _fetchConnection_DataArrived(IHwConnection conn, IRawData data)
        {
            foreach (IHwAddress a in _addressCollection)
                for (int i = 0; i < a.LenByte; i++)
                        _rawData[a.DataKey][a.StartByte + i] = _fetchConnection.RawData[a.DataKey][a.StartByte + i];

            if (DataArrived != null) DataArrived(this, _rawData );  
        }        


        public override void connect()
        {
            _timer.Enabled = true;
            _writeConnection.connect();
            _fetchConnection.connect();
        }

        public override void disconnect()
        {
            _timer.Enabled = false;
            _writeConnection.disconnect();
            _fetchConnection.disconnect();
        }

        public override void register(IHwSignal signal)
        {
            if (signal is S7Signal)
                if (!_signals.ContainsKey(signal.Name))
                {
                    _signals.Add(signal.Name, signal);
                    _addressCollection.Add(((S7Signal)signal).Address);
                    _fetchConnection.register(signal);
                    _writeConnection.register(signal);
                    signal.Connection = this;

                    //Controllo Integrità dati
                    IHwAddress a = ((S7Signal)signal).Address;
                    if (!_rawData.ContainsKey(a.DataKey))
                        _rawData.Add(a.DataKey, new Byte[4096]);
                    if (_rawData[a.DataKey].Length < a.LenByte + a.StartByte)
                        _rawData[a.DataKey] = new Byte[4096];
                }
        }

        public override event ConnectionStatus StatusChange;

        public override event ConnectionDataEvent DataArrived;
    }
}
